#include <math.h>
#include <stdio.h>


#include <util/mem.h>
#include <util/common_macros.h>
#include <util/msg.h>

#include "point.h"
#include "geometry.h"
#include "predicates.h"

#include "triangle.h"

static long int count = 0;

/* ========================================================================== */

// <editor-fold defaultstate="collapsed" desc="Inherent ObjectClass Methods">
static boolean __point_destroy(ImpObject * object) {
	ImpPoint * p;
	p = IMP_POINT(object);

	/* do nothing */
	p->x = 0;
	p->y = 0;
	p->z = 0;
	
	return TRUE;
}
// </editor-fold>

/* ========================================================================== */

// <editor-fold defaultstate="collapsed" desc="ObjectClass and Object initialization">
static void __point_class_init(ImpObjectClass * klass) {
	klass->object_clone = NULL;
	klass->object_destroy = __point_destroy;
	klass->object_attributes = NULL;
}
static void __point_init(ImpObject * object) {
	ImpPoint * p;
	p = IMP_POINT(object);

	p->x = 0;
	p->y = 0;
	p->z = 0;
}

// </editor-fold>
/* ========================================================================== */

ImpPointClass * imp_point_get_class(void) {
	static ImpPointClass * klass = NULL;

	if (klass == NULL) {
		ImpObjectClassInfo point_info = {
										"ImpPoint",
										sizeof (ImpPoint),
										sizeof (ImpPointClass),
										(ImpObjectClassInitFunc) __point_class_init,
										(ImpObjectInitFunc) __point_init,
										(ImpArgSetFunc) NULL,
										(ImpArgGetFunc) NULL
		};

		ImpObjectClass * parent_class = imp_object_get_class();
		ImpObjectClass * point_class = imp_object_class_new(parent_class, &point_info);
		klass = IMP_POINT_CLASS(point_class);
	}
	return klass;
}
ImpPoint * imp_point_new(real x, real y, real z) {
	ImpPoint * p;

	p = IMP_POINT(imp_object_new(IMP_OBJECT_CLASS(imp_point_get_class())));

	imp_point_set(p, x, y, z);
	p->id = count;
	count++;

	return p;
}
void imp_point_set(ImpPoint * p, real x, real y, real z) {
	p->x = x;
	p->y = y;
	p->z = z;
}
/* ========================================================================== */

/*
inline int imp_point_get_short(ImpPoint * p, long int i) {

	return ((short *) p->data)[i];
}
inline int imp_point_get_int(ImpPoint * p, long int i) {
	return ((int *) p->data)[i];
}
inline int imp_point_get_float(ImpPoint * p, long int i) {
	return ((float *) p->data)[i];
}
inline int imp_point_get_real(ImpPoint * p, long int i) {
	return ((real *) p->data)[i];
}
 */
/* ========================================================================== */

/*
inline int imp_point_set_short(ImpPoint * p, long int i, short val) {
	((short *) p->data)[i] = val;
}
inline int imp_point_set_int(ImpPoint * p, long int i, int val) {
	((int *) p->data)[i] = val;
}
inline int imp_point_set_float(ImpPoint * p, long int i, float val) {
	((float *) p->data)[i] = val;
}
inline int imp_point_set_real(ImpPoint * p, long int i, real val) {
	((real *) p->data)[i] = val;
}
 */
/* ========================================================================== */

real imp_point_distance2(ImpPoint * p1, ImpPoint * p2) {
	real x, y, z;

	_exit_if_fail(p1);
	_exit_if_fail(p2);

	x = p1->x - p2->x;
	y = p1->y - p2->y;
	z = p1->z - p2->z;

	return (x * x + y * y + z * z);
}
real imp_point_distance(ImpPoint * p1, ImpPoint * p2) {
	return sqrt(imp_point_distance2(p1, p2));
}
ImpOrientation imp_point_orientation(ImpPoint * a, ImpPoint * b, ImpPoint * c) {

	real or;

	or = _orient2d(&a->x, &b->x, &c->x);

	if (or > 0) return IMP_ORIENTATION_IN_LEFT;
	else if (or < 0) return IMP_ORIENTATION_IN_RIGHT;
	else if (or == 0) return IMP_ORIENTATION_COLLINEAR;

	return IMP_ORIENTATION_COLLINEAR;
}
boolean imp_point_in_left(ImpPoint * a, ImpPoint * b, ImpPoint * c) {
	return _orient2d(&a->x, &b->x, &c->x) > 0;
}
boolean imp_point_are_collinear(ImpPoint * a, ImpPoint * b, ImpPoint * c) {
	return _orient2d(&a->x, &b->x, &c->x) == 0;
}
boolean imp_point_in_right(ImpPoint * a, ImpPoint * b, ImpPoint * c) {
	return _orient2d(&a->x, &b->x, &c->x) < 0;
}
boolean imp_point_in_rect(ImpPoint * a, ImpPoint * b, ImpPoint * c) {
	return _in_rect(&a->x, &b->x, &c->x);
}
/* ========================================================================== */

real imp_point_slope(ImpPoint * p1, ImpPoint * p2) {

	return (p2->y - p1->y) / (p2->x - p1->x);
}
ImpPoint imp_point_mid_point(ImpPoint * a, ImpPoint * b) {
	ImpPoint p;
	p.x = (a->x + b->x) / 2;
	p.y = (a->y + b->y) / 2;
	p.z = (a->z + b->z) / 2;
	return p;
}
void imp_point_print(ImpPoint * p) {
	printf("(%.0f,%.0f)", p->x, p->y);
}
